Jusqu’à maintenant, nous avons empaqueté des widgets soit avec des boites verticales et horizontales, soit avec des tableaux. Vous pouvez, aussi positionner des widgets dans toutes les positions que vous voulez en utilisant un widget Fixed ou Layout. L'utilisation du widget Fixed n'est pas recommandé car il ne se redimensionne pas très bien.
Le conteneur disposition est similaire au conteneur fixé,à la différence qu'il implémente une zone de défilement infinie (en réalité limitée à 2^32).
Un widget disposition se crée avec:
layoutNew :: Maybe Adjustment -> Maybe Adjustment -> IO Layout
Comme vous pouvez le voir, on peut spécifier optionnellement les objets d'ajustement que le conteneur de disposition utilisera pour le défilement.
Vous pouvez ajouter et déplacer des widgets dans le conteneur disposition en utilisant les fonctions suivantes:
layoutPut :: (LayoutClass self, WidgetClass childWidget)=> self -> childWidget -> Int -> Int -> IO ()
layoutMove :: (LayoutClass self, WidgetClass childWidget)=> self -> childWidget -> Int -> Int -> IO ()
Le premier paramètre est la position en x, le deuxième la position en y. Le côté en haut à gauche est à (0,0), x augmente de gauche à droite et y de haut en bas.
La taille du conteneur Layout peut être définie en utilisant la fonction:
layoutSetSize :: LayoutClass self => self -> Int -> Int -> IO ()
Le premier paramètre est la largeur de la totalité de la zone pouvant défiler, le second, la hauteur.
Dans l'exemple, on place une liste d'étiquettes, chacune avec une lettre majuscule dans un cercle autour d'un centre. Les étiquettes sont positionnées perpendiculairement au rayon au moyen de la fonction :
labelSetAngle :: labelClass self => self -> Double -> IO ()
L'angle est en degrés, pris dans le sens trigonométrique (anti-horaire).
Le widget disposition est placé dans une fenêtre avec défilement au moyen de containerAdd car elle n'a pas besoin de view port (comme vu dans le chapitre 6.1). Les étiquettes sont positionnées en utilisant les coordonnées angulaires qui sont converties en coordonnées cartésiennes avec les fonctions sin et cos. Celles-ci prennent des radians comme arguments (entre 0 et 2*pi). Dans l'exemple, la largeur et la hauteur sont paramétrables, comme l'est la liste à afficher. De plus, dans la fonction main, les coins de Layout sont marqués, de sorte que vous puissiez facilement modifier sa taille si vous le souhaitez.
import Graphics.UI.Gtk
main :: IO ()
main = do
initGUI
window <- windowNew
set window [windowTitle := "Alphabet" , windowDefaultWidth := 350,
windowDefaultHeight := 350 , containerBorderWidth := 10]
sw <- scrolledWindowNew Nothing Nothing
set sw [scrolledWindowPlacement := CornerBottomRight,
scrolledWindowShadowType := ShadowEtchedIn,
scrolledWindowHscrollbarPolicy := PolicyAutomatic,
scrolledWindowVscrollbarPolicy := PolicyAutomatic ]
containerAdd window sw
layt <- layoutNew Nothing Nothing
layoutSetSize layt myLayoutWidth myLayoutHeight
widgetModifyBg layt StateNormal (Color 65535 65535 65535)
containerAdd sw layt
upleft <- labelNew (Just "+(0,0)")
layoutPut layt upleft 0 0
upright <- labelNew (Just ("+(" ++ (show (myLayoutWidth - 50)) ++",0)"))
layoutPut layt upright (myLayoutWidth -50) 0
dwnright <- labelNew (Just ("+(0," ++ (show (myLayoutHeight -20)) ++ ")"))
layoutPut layt dwnright 0 (myLayoutHeight -20)
dwnleft <- labelNew (Just ("+(" ++ (show(myLayoutWidth -70)) ++ "," ++
(show (myLayoutHeight -20)) ++ ")"))
layoutPut layt dwnleft (myLayoutWidth -70) (myLayoutHeight - 20)
labels <- sequence $ map (labelNew . Just) txtls
sequence_ $ map (\x -> widgetModifyFg x StateNormal (Color 0 0 45000)) labels
let wnums = zip labels [0..]
sequence_ $ map (myLayoutPut layt) wnums
widgetShowAll window
onDestroy window mainQuit
mainGUI
-- parameters
myLayoutWidth :: Int
myLayoutWidth = 800
myLayoutHeight :: Int
myLayoutHeight = 800
txtls :: [String]
txtls = map (\x -> x:[]) ['A'..'Z']
-- end parameters
step :: Double
step = (2 * pi)/(fromIntegral (length txtls))
ox :: Int
ox = myLayoutWidth `div` 2
oy :: Int
oy = myLayoutHeight `div` 2
radius :: Double
radius = 0.25 * (fromIntegral ox)
angle :: Int -> Double
angle num = 1.5 * pi + (fromIntegral num) * step
num2x :: Int -> Int
num2x n = ox + relx where
relx = round $ radius * (cos (angle n))
num2y :: Int -> Int
num2y n = oy + rely where
rely = round $ radius * (sin (angle n))
myLayoutPut :: Layout -> (Label, Int) -> IO ()
myLayoutPut lt (lb, n) = do
layoutPut lt lb (num2x n) (num2y n)
labelSetAngle lb (letterAngle n)
letterAngle :: Int -> Double
letterAngle n = (270 - degree) where
degree = (angle n) * (180.0 /pi)